package org.xwb.springcloud.messagetype.ack;

import com.rabbitmq.client.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;
import org.xwb.springcloud.constant.MqConstant;

import java.util.Date;

/**
 * @author 10564
 */
@Component
public class AckConsumer {
    private static final Logger log = LoggerFactory.getLogger(AckConsumer.class);

    @RabbitListener(queues = MqConstant.ACK_MQ_NAME)
    public void receiveAckQueueMessage(String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws  Exception {
        try {
            log.info("\nack消费者接收消息:{},tag:{} \n", message, tag);
            if("basicAck".equals( message)){
                //todo  确认消息处理成功，
                // 第一个参数 tag：消息的交付标签，唯一标识这条消息
                // 第二个参数 false：表示不批量确认，仅确认当前这条消息
                log.info("\n手动确认 处理成功 basicAck :{} \n", new Date());
                channel.basicAck(tag, false);
            }else if("basicNack".equals( message)){
                //todo 拒绝确认消息，即告诉 RabbitMQ 我们无法处理这条消息。这会导致该消息被重新放回队列中等待再次投递。
                //todo  1 第一个参数 表示消息的交付标签，是一个单调递增的标识符，用于唯一标识队列中的一条消息。通过这个标签，RabbitMQ 能够知道你正在处理哪一条消息。
                //todo  2 第二个参数 表示是否批量确认 true 表示否定确认当前 tag 以及之前所有未确认的消息；如果为 false，则只否定确认当前 tag 对应的消息。 此处为 false，代表只处理当前这一条消息
                //todo  3 第三个参数 如果设置为 true，消息会被重新放回队列，等待再次投递给其他消费者或同一个消费者。如果设置为 false，消息不会被重新入队，
                //todo 而是根据队列的配置可能会被丢弃或者路由到死信队列（如果配置了的话）。此处为 true，表示在发生异常时将消息重新入队以便重试。
                log.info("\n手动拒绝确认消息 basicNack :{} \n", new Date());
                channel.basicNack(tag, false, false);
            }else if("basicReject".equals( message)){
                //todo  拒绝消息，
                //todo  1、第一个参数是消息的交付标签，
                //todo  2、第二个参数表示是否将消息重新入队；false 表示拒绝后不重新入队，消息会被丢弃（如果没有配置死信队列，则可能被直接删除）
                log.info("\n手动确认 拒绝消息 basicReject :{} \n", new Date());
                channel.basicReject(tag, false);
            }else{
                throw new RuntimeException("模拟消费失败");
            }
        } catch (Exception e) {
            log.error("\n消费消息异常,抛出异常{} message：{}\n",tag, e.getMessage());
            //todo 抛出异常，触发 spring retry
            throw e;
        }
    }
}
